#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include "DGGSDefine.hpp"
#include "Utility.hpp"
using namespace NNU::DGGS;

/// <summary>
/// 程序运行功能选项
/// </summary>
enum Option
{
    CreateGlobalGrid,   // 生成全球格网
    CreatePartialGrid,  // 生成局部格网
    Grid2Feature,       // 格网转矢量
    Feature2Grid,       // 矢量转格网
    Field2Grid,         // 栅格转格网
    Grid2Field,         // 格网转栅格
    CreateFieldPyramid, // 生成多分辨率格网
    Unknown
};


/// <summary>
/// string转option
/// </summary>
Option toOption(const std::string &optionStr);


/// <summary>
/// 读取全球格网生成的参数配置文件并填充参数结构体
/// </summary>
void readGlobalGridParams(const char* paramsFileName, GlobalGridParams& params);

/// <summary>
/// 读取局部格网生成的参数配置文件并填充参数结构体
/// </summary>
void readPartitialGridParams(const char* paramsFileName, PartitialGridParams& params);

/// <summary>
/// 读取矢量转格网的参数配置文件并填充参数结构体
/// </summary>
void readOgr2DGGSParams(const char* paramsFileName, Ogr2DGGSParams& params);

/// <summary>
/// 读取格网转矢量的参数配置文件并填充参数结构体
/// </summary>
void readDGGS2OgrParams(const char* paramsFileName, DGGS2OgrParams& params);

/// <summary>
/// 读取栅格转格网的参数配置文件并填充参数结构体
/// </summary>
void readRaster2GridParams(const char* paramsFileName, Raster2GridParams& params);

/// <summary>
/// 读取格网转栅格的参数配置文件并填充参数结构体
/// </summary>
void readGrid2RasterParams(const char* paramsFileName, Grid2RasterParams& params);

/// <summary>
/// 读取生成多分辨率格网的参数配置文件并填充参数结构体
/// </summary>
void readFieldPyramidParams(const char* paramsFileName, FieldPyramidParams& params);


/// <summary>
/// 读取参数文件
/// </summary>
template<class T>
void readParams(const char* paramsFileName,T& params);



int main(int argc, char *argv[])
{
    //获取参数文件内容
    if(argc<=2)
        return 0;

    std::string optionString=argv[1];
    Option option= toOption(optionString);

    std::string txtFileName=argv[2];


    std::clock_t start = std::clock();
    switch (option)
    {
        case CreateGlobalGrid:
        {
            GlobalGridParams params;
            readParams(txtFileName.c_str(),params);
            if(!params.isValid())
                return -1;
            createGlobalGrid(params.outFileName.c_str(), params.level, params.elementType, params.gridType);
            break;
        }
        case CreatePartialGrid:
        {
            PartitialGridParams params;
            readParams(txtFileName.c_str(),params);
            if(!params.isValid())
                return -1;
            //获取边界包围盒
            std::ifstream file(params.inFileName);
            if (!file) {
                throw std::runtime_error("Unable to open file: " + params.inFileName);
            }
            double boundBox[4];
            for (int i = 0; i < 4 && file >> boundBox[i]; ++i);
            //生成格网
            createPartialGrid(params.outFileName.c_str(), params.level, params.elementType, params.gridType, boundBox);
            break;
        }
        case Feature2Grid:
        {
            Ogr2DGGSParams params;
            readParams(txtFileName.c_str(),params);
            if(!params.isValid())
                return -1;
            OGRFile2DGGSFile(params.inFileName.c_str(),params.outFileName.c_str(),params.level,params.gridType,params.elementType);
            break;
        }
        case Grid2Feature:
        {
            DGGS2OgrParams params;
            readParams(txtFileName.c_str(),params);
            if(!params.isValid())
                return -1;
            DGGSFile2OGRFile(params.inFileName.c_str(),params.outFileName.c_str());
            break;
        }
        case Field2Grid:
        {
            Raster2GridParams params;
            readParams(txtFileName.c_str(),params);
            if(!params.isValid())
                return -1;
            GDALRaster2DGGSField(params.inFileName.c_str(), params.outFileName.c_str(), params.gridType,params.elementType,params.level);
            break;
        }
        case Grid2Field:
        {
            Grid2RasterParams params;
            readParams(txtFileName.c_str(),params);
            if(!params.isValid())
                return -1;
            DGGSField2GDALRaster(params.inFileName.c_str(), params.outFileName.c_str());
            break;
        }
        case CreateFieldPyramid:
        {
            FieldPyramidParams params;
            readParams(txtFileName.c_str(),params);
            if(!params.isValid())
                return -1;
            createFieldPyramid(params.inFileName.c_str(), params.outFileName.c_str(), params.tileFileSize,params.minLevel,params.maxLevel);
            break;
        }
        case Unknown:
            break;

    }

    std::clock_t end = std::clock();
    double elapsed = double(end - start) / CLOCKS_PER_SEC;

    std::cout << "spend time: " << elapsed << " 秒" << std::endl;
    return 0;
}


Option toOption(const std::string &optionStr)
{
    Option option=Option::Unknown;
    if(optionStr=="CreateGlobalGrid")
        option=Option::CreateGlobalGrid;
        //生成局部离散格网
    else if(optionStr=="CreatePartialGrid")
        option=Option::CreatePartialGrid;
        //格网转矢量
    else if(optionStr=="Grid2Feature")
        option=Option::Grid2Feature;
        //矢量转格网
    else if(optionStr=="Feature2Grid")
        option=Option::Feature2Grid;
        //格网转栅格
    else if(optionStr=="Grid2Field")
        option=Option::Grid2Field;
        //栅格转格网
    else if(optionStr=="Field2Grid")
        option=Option::Field2Grid;
    else if(optionStr=="CreateFieldPyramid")
        option=Option::CreateFieldPyramid;
    return option;
}

template<class T>
void readParams(const char* paramsFileName,T& params)
{
    std::cout << "Reading Params..." << std::endl;
    if constexpr (std::is_same<T, GlobalGridParams>::value)
    {
        readGlobalGridParams(paramsFileName,params);
    }
    else if constexpr (std::is_same<T, PartitialGridParams>::value)
    {
        readPartitialGridParams(paramsFileName,params);
    }
    else if constexpr (std::is_same<T, Ogr2DGGSParams>::value)
    {
        readOgr2DGGSParams(paramsFileName,params);
    }
    else if constexpr (std::is_same<T, DGGS2OgrParams>::value)
    {
        readDGGS2OgrParams(paramsFileName,params);
    }
    else if constexpr (std::is_same<T, Raster2GridParams>::value)
    {
        readRaster2GridParams(paramsFileName, params);
    }
    else if constexpr (std::is_same<T, Grid2RasterParams>::value)
    {
        readGrid2RasterParams(paramsFileName,params);
    }
    else if constexpr (std::is_same<T, FieldPyramidParams>::value)
    {
        readFieldPyramidParams(paramsFileName,params);
    }
}

void readGlobalGridParams(const char* paramsFileName, GlobalGridParams& params)
{
    std::ifstream infile(paramsFileName);
    std::string line;
    while (std::getline(infile, line)) {
        line.erase(remove_if(line.begin(), line.end(), isspace), line.end());
        size_t pos = line.find(":");
        if (pos != std::string::npos) {
            std::string key = line.substr(0, pos);
            std::string value = line.substr(pos + 1);
            if ("outFileName" == key)
                params.outFileName = value;
            else if ("level" == key)
                params.level = std::stoi(value);
            else if ("elementType" == key)
                params.elementType = getElementTypeFromStr(value);  // 需要实现转换函数
            else if ("gridType" == key)
                params.gridType = getGridTypeFromStr(value);  // 需要实现转换函数
        }
    }
    infile.close();
}

void readPartitialGridParams(const char* paramsFileName, PartitialGridParams& params)
{
    std::ifstream infile(paramsFileName);
    std::string line;
    while (std::getline(infile, line)) {
        line.erase(remove_if(line.begin(), line.end(), isspace), line.end());
        size_t pos = line.find(":");
        if (pos != std::string::npos) {
            std::string key = line.substr(0, pos);
            std::string value = line.substr(pos + 1);
            if ("outFileName" == key)
                params.outFileName = value;
            else if ("level" == key)
                params.level = std::stoi(value);
            else if ("elementType" == key)
                params.elementType = getElementTypeFromStr(value);
            else if ("gridType" == key)
                params.gridType = getGridTypeFromStr(value);
            else if ("inFileName" == key) {
                params.inFileName = value;
            }
        }
    }
    infile.close();
}

void readOgr2DGGSParams(const char* paramsFileName, Ogr2DGGSParams& params)
{
    std::ifstream infile(paramsFileName);
    std::string line;
    while (std::getline(infile, line)) {
        line.erase(remove_if(line.begin(), line.end(), isspace), line.end());
        size_t pos = line.find(":");
        if (pos != std::string::npos) {
            std::string key = line.substr(0, pos);
            std::string value = line.substr(pos + 1);
            if ("inFileName" == key)
                params.inFileName = value;
            else if ("outFileName" == key)
                params.outFileName = value;
            else if ("level" == key)
                params.level = std::stoi(value);
            else if ("elementType" == key)
                params.elementType = getElementTypeFromStr(value);
            else if ("gridType" == key)
                params.gridType = getGridTypeFromStr(value);
        }
    }
    infile.close();
}

void readDGGS2OgrParams(const char* paramsFileName, DGGS2OgrParams& params)
{
    std::ifstream infile(paramsFileName);
    std::string line;
    while (std::getline(infile, line)) {
        line.erase(remove_if(line.begin(), line.end(), isspace), line.end());
        size_t pos = line.find(":");
        if (pos != std::string::npos) {
            std::string key = line.substr(0, pos);
            std::string value = line.substr(pos + 1);
            if ("inFileName" == key)
                params.inFileName = value;
            else if ("outFileName" == key)
                params.outFileName = value;
        }
    }
    infile.close();
}

void readRaster2GridParams(const char* paramsFileName, Raster2GridParams& params)
{
    std::ifstream infile(paramsFileName);
    std::string line;
    while (std::getline(infile, line)) {
        line.erase(remove_if(line.begin(), line.end(), isspace), line.end());
        size_t pos = line.find(":");
        if (pos != std::string::npos) {
            std::string key = line.substr(0, pos);
            std::string value = line.substr(pos + 1);
            if ("inFileName" == key)
                params.inFileName = value;
            else if ("outFileName" == key)
                params.outFileName = value;
            else if ("elementType" == key)
                params.elementType = getElementTypeFromStr(value);
            else if ("gridType" == key)
                params.gridType = getGridTypeFromStr(value);
            else if ("level" == key)
                params.level = std::stoi(value);
        }
    }
    infile.close();
}


void readGrid2RasterParams(const char* paramsFileName, Grid2RasterParams& params)
{
    std::ifstream infile(paramsFileName);
    std::string line;
    while (std::getline(infile, line)) {
        line.erase(remove_if(line.begin(), line.end(), isspace), line.end());
        size_t pos = line.find(":");
        if (pos != std::string::npos) {
            std::string key = line.substr(0, pos);
            std::string value = line.substr(pos + 1);
            if ("inFileName" == key)
                params.inFileName = value;
            else if ("outFileName" == key)
                params.outFileName = value;
            else if ("elementType" == key)
                params.elementType = getElementTypeFromStr(value);
            else if ("gridType" == key)
                params.gridType = getGridTypeFromStr(value);
        }
    }
    infile.close();
}

void readFieldPyramidParams(const char* paramsFileName, FieldPyramidParams& params)
{
    std::ifstream infile(paramsFileName);
    std::string line;
    while (std::getline(infile, line)) {
        line.erase(remove_if(line.begin(), line.end(), isspace), line.end());
        size_t pos = line.find(":");
        if (pos != std::string::npos) {
            std::string key = line.substr(0, pos);
            std::string value = line.substr(pos + 1);
            if ("inFileName" == key)
                params.inFileName = value;
            else if ("outFileName" == key)
                params.outFileName = value;
            else if ("tileFileSize" == key)
                params.tileFileSize = std::stod(value);
            else if ("minLevel" == key)
                params.minLevel = std::stoi(value);
            else if ("maxLevel" == key)
                params.maxLevel = std::stoi(value);
        }
    }
    infile.close();
}
